﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Web;
using Medianamik.Core;
using Medianamik.Core.DAL;
using Medianamik.Core.DAL.Repositories;
using Medianamik.Core.Sugar;
using Medianamik.UI.Web.Shop.Data.Dto;
using Medianamik.UI.Web.Shop.Data.Interfaces;
using Medianamik.UI.Web.Shop.Data.Repositories;
using SubSonic;
using Medianamik.UI.Web.Shop.Data;

namespace Medianamik.UI.Web.Shop.Services
{
    public interface IShoppingCartService<T, TItem, TProduct, TModel, TShoppingCartItemWithModels>
        where T : IShoppingCart<TItem, TModel>
        where TItem: IShoppingCartItem<TModel>
        where TModel : Data.Interfaces.IModel
        where TProduct : Data.Interfaces.IProduct
    {
        void UpdateShoppingCart(T shoppingCart);
        bool UpdateShoppinCartItemQuantity(Guid shoppingCartItemId, int quantity);
        IList<TShoppingCartItemWithModels> GetShoppingCartItems();
        bool DeleteShoppingCartItem(Guid shoppingCartItemId);
        void DeleteShoppingCart();
        void DeleteShoppingCart(Guid shoppingCartId);
        void UpdateOrAddShoppingCartItem(TProduct product, IList<TModel> models);
        void UpdateOrAddShoppingCartItem(TProduct product);
        double GetShoppingCartItemsTotalPrice();
        double GetShoppingCartItemsTotalPrice(IList<TShoppingCartItemWithModels> shoppingCartItems);
        IShoppingCartRepository<T, TItem, TModel> ShoppingCartRepository { get; }
        T CurrentShoppingCart { get; }
        T CurrentShoppingCartUpdated { get; }
        T GetShoppingCartByOrderId(Guid orderId);
    }

    public abstract class ShoppingCartService<T, TItem, TProduct, TModel, TShoppingCartItemWithModels> : IShoppingCartService<T, TItem, TProduct, TModel, TShoppingCartItemWithModels>
        where T : class, IShoppingCart<TItem, TModel>, new() 
        where TItem: class, IShoppingCartItem<TModel>, new() 
        where TModel : Data.Interfaces.IModel
        where TShoppingCartItemWithModels : IShoppingCartItemWithModels<TModel>, new()
        where TProduct : Data.Interfaces.IProduct
    {
        private readonly IShoppingCartRepository<T, TItem, TModel> _shoppingCartRepository;
        private readonly INodeRepository _nodeRepository;
        private readonly INodeTypeRepository _nodeTypeRepository;
        private readonly IShoppingCartItemRepository<TItem, TModel> _shoppingCartItemRepository;

        public ShoppingCartService(IShoppingCartRepository<T, TItem, TModel> shoppingCartRepository,
            INodeRepository nodeRepository, INodeTypeRepository nodeTypeRepository,
            IShoppingCartItemRepository<TItem, TModel> shoppingCartItemRepository)
        {
            _shoppingCartRepository = shoppingCartRepository;
            _nodeRepository = nodeRepository;
            _nodeTypeRepository = nodeTypeRepository;
            _shoppingCartItemRepository = shoppingCartItemRepository;
        }

        public virtual IShoppingCartRepository<T, TItem, TModel> ShoppingCartRepository
        {
            get { return _shoppingCartRepository; }
        }

        protected virtual T GetShoppingCartFromCookieOrCreateIt()
        {
            var shoppingCartCookie = HttpContext.Current.Request.Cookies[Constants.ShoppingCartCookieName];
            Guid shoppingCartId;

            if (shoppingCartCookie != null && !String.IsNullOrEmpty(shoppingCartCookie.Value) &&
                StringHelper.IsGuid(shoppingCartCookie.Value, out shoppingCartId))
            {
                var shoppingCart = GetShoppingCart(shoppingCartId);
                if (shoppingCart.CompletedDate == null)
                    return shoppingCart;
            }

            return CreateShoppingCart();
        }

        protected virtual T CreateShoppingCart()
        {
            var shoppingCart = new T
                  {
                      NodeId = Guid.NewGuid(),
                      CultureName = CultureInfo.InvariantCulture.Name,
                      TypeId = ShoppingCartTypeId
                  };
            shoppingCart.Name = shoppingCart.NodeId.ToString();
            shoppingCart = CreateOrUpdateShoppingCart(shoppingCart);
            var shoppingCartCookie = new HttpCookie(Constants.ShoppingCartCookieName,
                                                shoppingCart.NodeId.ToString());
            HttpContext.Current.Response.Cookies.Add(shoppingCartCookie);
            return shoppingCart;
        }

        protected virtual Guid ShoppingCartTypeId
        {
            get { return TypeIds.ShoppingCart; }
        }

        public virtual T GetShoppingCart(Guid shoppingCartId)
        {
            var shoppingCart = _shoppingCartRepository.Get(shoppingCartId);

            return ShoppingCartToDto(shoppingCart ?? CreateShoppingCart(),
                 _shoppingCartItemRepository.ChildsOf(shoppingCartId, 
                 false, 1, CultureInfo.InvariantCulture, ContentState.Draft, ActiveStatus.Activated));
        }

        public T GetShoppingCartByOrderId(Guid orderId)
        {
            return _shoppingCartRepository.GetShoppingCartByOrderId(orderId);
        }

        protected virtual T CreateOrUpdateShoppingCart(T shoppingCartDto)
        {
            var mapper = new NodeMapper(_shoppingCartRepository, _nodeTypeRepository);
            var mappedNode = mapper.MapNode(shoppingCartDto);
            _shoppingCartRepository.SaveNode(mappedNode);
            return shoppingCartDto;
        }

        private static T ShoppingCartToDto(IShoppingCart<TItem, TModel> shoppingCart, IList<TItem> shoppingCartItems)
        {
            if (shoppingCart == null) throw new ArgumentNullException("shoppingCart");
            return  new T
               {
                   Order = shoppingCart.Order,
                   Client = shoppingCart.Client,
                   CurrentStep = shoppingCart.CurrentStep,
                   NodeId = shoppingCart.NodeId,
                   CompletedDate = shoppingCart.CompletedDate,
                   CreatedOn = shoppingCart.CreatedOn,
                   CultureName = shoppingCart.CultureName,
                   Depth = shoppingCart.Depth,
                   Generation = shoppingCart.Generation,
                   HasModifications = shoppingCart.HasModifications,
                   IsActive = shoppingCart.IsActive,
                   ModifiedOn = shoppingCart.ModifiedOn,
                   Name = shoppingCart.Name,
                   ParentNodeId = shoppingCart.ParentNodeId,
                   Path = shoppingCart.Path,
                   Position = shoppingCart.Position,
                   Revision = shoppingCart.Revision,
                   TypeId = shoppingCart.TypeId,
                   ShoppingCartItems = shoppingCartItems
               };
        }

        public virtual void UpdateShoppingCart(T shoppingCart)
        {
            CreateOrUpdateShoppingCart(shoppingCart);
        }

        public virtual void AddShoppingCartItem(TProduct product, int quantity, IList<TModel> models)
        {
            var itemNodeId = Guid.NewGuid();
            var item = new TItem
            {
                Product = product.NodeId,
                Quantity = quantity,
                ParentNodeId = CurrentShoppingCart.NodeId,
                TypeId = ShoppingCartItemTypeId,
                //Price = product.Price.GetValueOrDefault(0),
                Name = itemNodeId.ToString(),
                NodeId = itemNodeId,
                CultureName = CultureInfo.InvariantCulture.Name
            };

            var nodes = new List<Node>();
            var nodeShoppingCartItem = new NodeMapper(_nodeRepository, _nodeTypeRepository)
                .MapNode(item).InnerNode;
            nodes.Add(nodeShoppingCartItem);

            foreach (var model in models)
            {
                var modelsInShoppingCartItems = new ModelsInShoppingCartItems
                {
                    Models = model.NodeId,
                    ShoppingCartItems = nodeShoppingCartItem.ID,
                    ParentNodeId = item.ParentNodeId,
                    TypeId = ModelsInShoppingCartItems.TYPE_ID,
                    CultureName = CultureInfo.InvariantCulture.Name
                    
                };
                nodes.Add(new NodeMapper(_nodeRepository, _nodeTypeRepository)
                    .MapNode(modelsInShoppingCartItems).InnerNode);
            }
            NodeManager.SaveNodes(nodes);
        }

        protected virtual Guid ShoppingCartItemTypeId
        {
            get { return TypeIds.ShoppingCartItem; }
        }

        #region UpdateShoppingCartItem

        public virtual bool UpdateShoppinCartItemQuantity(Guid shoppingCartItemId, int quantity)
        {
            var updated = false;
            var shoppingCartItem = _shoppingCartItemRepository.Get(shoppingCartItemId);
            if (shoppingCartItem != null)
            {
                shoppingCartItem.Quantity = quantity;
                NodeManager.SaveNode(new NodeMapper(_nodeRepository, _nodeTypeRepository).MapNode(shoppingCartItem));
                updated = true;
            }
            return updated;
        }

        public virtual void UpdateOrAddShoppingCartItem(TProduct product)
        {
            UpdateOrAddShoppingCartItem(product, new List<TModel>());
        }

        public virtual void UpdateOrAddShoppingCartItem(TProduct product, IList<TModel> models)
        {
            var shoppingCartItem = Get(CurrentShoppingCart.NodeId, product.NodeId, models);

            if (shoppingCartItem != null)
            {
                UpdateShoppinCartItemQuantity(shoppingCartItem.NodeId, shoppingCartItem.Quantity.GetValueOrDefault(0) + 1);
            }
            else
            {
                AddShoppingCartItem(product, 1, models);
            }
        }

        protected virtual TItem Get(Guid shoppingCartId, Guid productId, IList<TModel> models)
        {
            return models.Count == 0 ? GetShoppingCartItemWithoutModel(shoppingCartId, productId) :
                GetShoppingCartItemWithModel(shoppingCartId, productId, models);
        }

        protected virtual TItem GetShoppingCartItemWithoutModel(Guid shoppingCartId, Guid productId)
        {
            var query = new Select("*")
                .Top("1")
                .From(ShopSchema.ShoppingCartItem.TableName)
                .Where(ShopSchema.BaseColumns.ParentNodeId).IsEqualTo(shoppingCartId)
                .And(ShopSchema.ShoppingCartItem.Columns.Product).IsEqualTo(productId.ToString())
                .And(ShopSchema.BaseColumns.Generation).IsEqualTo(ContentState.Draft);

            return query.ExecuteSingle<TItem>();
        }

        protected virtual TItem GetShoppingCartItemWithModel(Guid shoppingCartId, Guid productId, IList<TModel> models)
        {
            var oldShoppingCartItemId = "";
            var modelCount = models.Count;
            var modelEqual = 0;
            TItem shoppingCartItem = null;

            var query = new Select(ShopSchema.ShoppingCartItem.TableName + ".*", 
                        ShopSchema.ShoppingCartItem.Columns.Quantity, ShopSchema.ModelsInShoppingCartItems.Columns.Models)
                .From(ShopSchema.ShoppingCartItem.TableName)
                .InnerJoin(ShopSchema.ModelsInShoppingCartItems.TableName,
                   ShopSchema.ModelsInShoppingCartItems.Columns.ShoppingCartItems,
                   ShopSchema.ShoppingCartItem.TableName, ShopSchema.BaseColumns.NodeId)
                .Where(ShopSchema.BaseColumns.ParentNodeId).IsEqualTo(shoppingCartId)
                .And(ShopSchema.ShoppingCartItem.Columns.Product).IsEqualTo(productId.ToString())
                .And(ShopSchema.BaseColumns.Generation).IsEqualTo(ContentState.Draft)
                .And(ShopSchema.BaseColumns.Generation).IsEqualTo(ContentState.Draft)
                .OrderAsc(ShopSchema.BaseColumns.NodeId);

            using (var reader = query.ExecuteReader())
            {
                while (reader.Read() && modelEqual != modelCount)
                {
                    if (oldShoppingCartItemId != reader[ShopSchema.BaseColumns.NodeId].ToString())
                    {
                        modelEqual = 0;
                        oldShoppingCartItemId = reader[ShopSchema.BaseColumns.NodeId].ToString();
                        shoppingCartItem = new TItem
                        {
                            TypeId = new Guid(reader[ShopSchema.BaseColumns.TypeId].ToString()),
                            NodeId = new Guid(reader[ShopSchema.BaseColumns.NodeId].ToString()),
                            Product = new Guid(reader[ShopSchema.ShoppingCartItem.Columns.Product].ToString()),
                            Quantity = (int)reader[ShopSchema.ShoppingCartItem.Columns.Quantity]
                        };
                    }

                    foreach (var model in models)
                    {
                        if (Equals(model.NodeId.ToString(),
                                   reader[ShopSchema.ModelsInShoppingCartItems.Columns.Models].ToString()))
                            modelEqual += 1;
                    }
                }

                if (modelEqual != modelCount)
                    shoppingCartItem = null;
            }

            return shoppingCartItem;
        }

        #endregion

        #region GetShoppingCartItems

        public virtual IList<TShoppingCartItemWithModels> GetShoppingCartItems()
        {
            var shoppingCartItems = GetShoppingCartItemWithModels();
            shoppingCartItems.AddRange(GetShoppingCartItemsWithoutModel());
            return shoppingCartItems;
        }

        protected virtual IEnumerable<TShoppingCartItemWithModels> GetShoppingCartItemsWithoutModel()
        {
            var shoppingCartItemsViewModel = new List<TShoppingCartItemWithModels>();
            var query = GetQueryShoppingCartItemWithoutModel();

            using (var reader = query.ExecuteReader())
            {
                while (reader.Read())
                {
                    shoppingCartItemsViewModel.Add(GetShoppingCartItemDto(reader));
                }
            }
            return shoppingCartItemsViewModel;
        }

        protected virtual SqlQuery GetQueryShoppingCartItemWithoutModel()
        {
            return new Select(ShopSchema.BaseColumns.Name,
                              ShopSchema.ShoppingCartItem.Columns.Quantity,
                              ShopSchema.BaseColumns.NodeId)
                .From(ShopSchema.ShoppingCartItem.TableName)
                .InnerJoin(ShopSchema.Product.TableName, ShopSchema.BaseColumns.NodeId,
                           ShopSchema.ShoppingCartItem.TableName, ShopSchema.ShoppingCartItem.Columns.Product)
                .LeftOuterJoin(ShopSchema.ModelsInShoppingCartItems.TableName,
                               ShopSchema.ModelsInShoppingCartItems.Columns.ShoppingCartItems,
                               ShopSchema.ShoppingCartItem.TableName, ShopSchema.BaseColumns.NodeId)
                .Where(ShopSchema.BaseColumns.ParentNodeId).IsEqualTo(CurrentShoppingCart.NodeId)
                .And(ShopSchema.ModelsInShoppingCartItems.Columns.ShoppingCartItems).IsNull()
                .And(ShopSchema.BaseColumns.Generation).IsEqualTo(ContentState.Draft)
                .And(ShopSchema.BaseColumns.CultureName).IsEqualTo(CultureInfo.CurrentCulture.Name);
        }

        protected virtual List<TShoppingCartItemWithModels> GetShoppingCartItemWithModels()
        {
            var shoppingCartItems = new List<TShoppingCartItemWithModels>();
            var shoppingCartItem = new TShoppingCartItemWithModels();

            var shoppingCartItemId = "";
            var query = GetQueryShoppingCartItemWithModel();

            using (var reader = query.ExecuteReader())
            {
                while (reader.Read())
                {
                    var model = GetModel(reader);

                    if (shoppingCartItemId != reader[ShopSchema.BaseColumns.NodeId].ToString())
                    {
                        shoppingCartItemId = reader[ShopSchema.BaseColumns.NodeId].ToString();
                        shoppingCartItem = GetShoppingCartItemDto(reader);
                        shoppingCartItem.Price += model.PriceVariant.GetValueOrDefault(0);
                        shoppingCartItem.Models.Add(model);
                        shoppingCartItems.Add(shoppingCartItem);
                    }
                    else
                    {
                        shoppingCartItem.Price += model.PriceVariant.GetValueOrDefault(0);
                        shoppingCartItem.Models.Add(model);
                    }
                }
            }
            return shoppingCartItems;
        }

        protected abstract SqlQuery GetQueryShoppingCartItemWithModel();
        //{
        //    throw new NotImplementedException();
            /*return new Select(
                ProductForSale.Schema.GetColumn(ProductForSale.Columns.Name).QualifiedName,
                ProductForSale.Columns.SmallImage,
                ProductForSale.Columns.Price,
                TItem.Columns.Quantity,
                TItem.Columns.NodeId,
                TModel.Schema.GetColumn(TModel.Columns.Name).QualifiedName + "as " + beginModelAlias,
                TModel.Schema.GetColumn(TModel.Columns.PriceVariant).QualifiedName,
                TModel.Schema.GetColumn(TModel.Columns.NodeId).QualifiedName)
                .From(TItem.Schema)
                .InnerJoin(ProductForSale.Schema.TableName,
                           ProductForSale.Columns.NodeId,
                           TItem.Schema.TableName,
                           TItem.Columns.Product)
                .LeftOuterJoin(ModelsInShoppingCartItems.Schema.TableName,
                               ModelsInShoppingCartItems.Columns.ShoppingCartItems,
                               TItem.Schema.TableName,
                               TItem.Columns.NodeId)
                .InnerJoin(TModel.Schema.TableName,
                           TModel.Columns.NodeId,
                           ModelsInShoppingCartItems.Schema.TableName,
                           ModelsInShoppingCartItems.Columns.Models)
                .Where(TItem.Columns.ParentNodeId).IsEqualTo(GetShoppingCartFromCookieOrCreateIt().NodeId)
                .And(ModelsInShoppingCartItems.Columns.Generation).IsEqualTo(ContentState.Published)
                .And(ProductForSale.Schema.GetColumn(ProductForSale.Columns.Generation)).IsEqualTo(ContentState.Published)
                .And(TModel.Schema.GetColumn(TModel.Columns.Generation)).IsEqualTo(ContentState.Published)
                .And(ModelsInShoppingCartItems.Schema.GetColumn(ModelsInShoppingCartItems.Columns.Generation)).IsEqualTo(ContentState.Published)
                .And(ProductForSale.Schema.GetColumn(ProductForSale.Columns.CultureName)).IsEqualTo(CultureInfo.CurrentCulture.Name)
                .OrderAsc(TItem.Schema.GetColumn(TItem.Columns.NodeId).QualifiedName);*/
        //}

        protected virtual TShoppingCartItemWithModels GetShoppingCartItemDto(IDataRecord reader)
        {
            throw new NotImplementedException();
        }

        protected virtual TModel GetModel(IDataRecord reader)
        {
            throw new NotImplementedException();
        }
        #endregion

        protected T _currentShoppingCart;
        public virtual T CurrentShoppingCart
        {
            get
            {
                return _currentShoppingCart ?? (_currentShoppingCart =
                    GetShoppingCartFromCookieOrCreateIt());
            }
        }

        public virtual T CurrentShoppingCartUpdated
        {
            get
            {
                _currentShoppingCart = GetShoppingCartFromCookieOrCreateIt();
                return _currentShoppingCart;
            }
        }

        public virtual bool DeleteShoppingCartItem(Guid shoppingCartItemId)
        {
            var deleted = false;
            if (_shoppingCartItemRepository.Get(shoppingCartItemId) != null)
            {
                NodeManager.DestroyNode(shoppingCartItemId);
                deleted = true;
            }
            return deleted;
        }

        public virtual void DeleteShoppingCart()
        {
            DeleteShoppingCart(CurrentShoppingCart.NodeId);
        }

        public virtual void DeleteShoppingCart(Guid shoppingCartId)
        {
            NodeManager.DeleteBranch(shoppingCartId);
        }

        public virtual double GetShoppingCartItemsTotalPrice()
        {
            return GetShoppingCartItemsTotalPrice(GetShoppingCartItems());
        }

        public virtual double GetShoppingCartItemsTotalPrice(IList<TShoppingCartItemWithModels> shoppingCartItems)
        {
            return shoppingCartItems.Sum(shoppingCartItem => shoppingCartItem.SubTotal);
        }
    }
    public class ShoppingCartService : ShoppingCartService<ShoppingCart, ShoppingCartItem,
        BaseProduct, Model, ShoppingCartItemWithModels>
    {
        public ShoppingCartService(IShoppingCartRepository<ShoppingCart, ShoppingCartItem, Model> 
            shoppingCartRepository, INodeRepository nodeRepository, 
            INodeTypeRepository nodeTypeRepository, IShoppingCartItemRepository<ShoppingCartItem, Model> 
            shoppingCartItemRepository) : base(shoppingCartRepository, nodeRepository, nodeTypeRepository, 
            shoppingCartItemRepository)
        {
        }

        protected override SqlQuery GetQueryShoppingCartItemWithModel()
        {
            throw new NotImplementedException();
            /*return new Select(
    ProductForSale.Schema.GetColumn(ProductForSale.Columns.Name).QualifiedName,
    ProductForSale.Columns.SmallImage,
    ProductForSale.Columns.Price,
    TItem.Columns.Quantity,
    TItem.Columns.NodeId,
    TModel.Schema.GetColumn(TModel.Columns.Name).QualifiedName + "as " + beginModelAlias,
    TModel.Schema.GetColumn(TModel.Columns.PriceVariant).QualifiedName,
    TModel.Schema.GetColumn(TModel.Columns.NodeId).QualifiedName)
    .From(TItem.Schema)
    .InnerJoin(ProductForSale.Schema.TableName,
               ProductForSale.Columns.NodeId,
               TItem.Schema.TableName,
               TItem.Columns.Product)
    .LeftOuterJoin(ModelsInShoppingCartItems.Schema.TableName,
                   ModelsInShoppingCartItems.Columns.ShoppingCartItems,
                   TItem.Schema.TableName,
                   TItem.Columns.NodeId)
    .InnerJoin(TModel.Schema.TableName,
               TModel.Columns.NodeId,
               ModelsInShoppingCartItems.Schema.TableName,
               ModelsInShoppingCartItems.Columns.Models)
    .Where(TItem.Columns.ParentNodeId).IsEqualTo(GetShoppingCartFromCookieOrCreateIt().NodeId)
    .And(ModelsInShoppingCartItems.Columns.Generation).IsEqualTo(ContentState.Published)
    .And(ProductForSale.Schema.GetColumn(ProductForSale.Columns.Generation)).IsEqualTo(ContentState.Published)
    .And(TModel.Schema.GetColumn(TModel.Columns.Generation)).IsEqualTo(ContentState.Published)
    .And(ModelsInShoppingCartItems.Schema.GetColumn(ModelsInShoppingCartItems.Columns.Generation)).IsEqualTo(ContentState.Published)
    .And(ProductForSale.Schema.GetColumn(ProductForSale.Columns.CultureName)).IsEqualTo(CultureInfo.CurrentCulture.Name)
    .OrderAsc(TItem.Schema.GetColumn(TItem.Columns.NodeId).QualifiedName);*/
        }
    }

    public interface IShoppingCartService : IShoppingCartService<ShoppingCart, ShoppingCartItem, BaseProduct, Model, ShoppingCartItemWithModels>
    {}


}
